Skip to content

chore(release): 0.16.4 — ADR-037 Slice B (wire-evidence echo on /gate) - #96

Merged
maltsev-dev merged 1 commit into
masterfrom
release/0.16.4
Sep 1, 2026
Merged

chore(release): 0.16.4 — ADR-037 Slice B (wire-evidence echo on /gate)#96
maltsev-dev merged 1 commit into
masterfrom
release/0.16.4

Conversation

@maltsev-dev

Copy link
Copy Markdown
Member

Patch release — ADR-037 Slice B. Wire protocol 3 → 4 additively. /gate response now echoes the SDK-supplied action_digest and a policy_hash slot (always None today; Slice D wires per-request computation). min_protocol_version stays at 2 so v3 SDKs are unaffected. Wire-format additive only — no new hashing/computation introduced on either side (both fields echo already-computed values).

Highlights

  • NULLRUN_PROTOCOL_VERSION = 4 — single source of truth at src/nullrun/transport.py; tests pin to str(NULLRUN_PROTOCOL_VERSION) so a future bump doesn't sweep this file again. NullRunProtocolError.user_action + docs/errors/NR-P001.md updated to point operators at X-NULLRUN-PROTOCOL: 4.
  • runtime._capture_wire_evidence — reads action_digest + policy_hash off the /gate response and stores into two new contextvars (_last_gate_action_digest_var, _last_gate_policy_hash_var). Defensive: drops non-str at WARNING, never raises. Called from _capture_server_minted_execution_id so execution_id and action_digest always refer to the same /check decision.
  • ServerCapabilities.wire_evidence_echo — informational flag surfaced by /api/v1/capabilities. NOT in is_v3_ready() (informational, not a hard gate). Defaults to False on pre-v4 backends.
  • tests/test_slice_b_wire_evidence.py (10 new tests) — pins the SDK-side of the v3→4 additive bump.
  • tests/test_capabilities.py — 2 new assertions: test_parse_capabilities_wire_evidence_echo_v4_backend + test_parse_capabilities_v4_protocol_range.
  • tests/contract/test_audit_wire.py + tests/test_v3_wire_contract.py — header assertions now source str(NULLRUN_PROTOCOL_VERSION) instead of the literal "3" so a future bump doesn't require sweeping either file.

Verification

  • Targeted suite: tests/test_slice_b_wire_evidence.py — 10/10 pass.
  • Broader regression suite: pytest -q 1613 passed / 4 skipped (12 more than 0.16.3).
  • ruff check src tests all checks pass.
  • mypy src/nullrun no issues reported in 37 source files.

Refs: ADR-037 Slice B (2026-08-31); backend commit 8dbeaf4d (parity CI test analogue); docs/errors/NR-P001.md.

* feat(sdk): wire-evidence echo capture from /gate response (ADR-037 Slice B)

Implements the SDK-side of the protocol-3→4 additive bump. The
backend's /gate response now echoes the SDK-supplied action_digest
and a policy_hash slot (None today; Slice D wires per-request
computation). Pre-v4 backends omit both keys entirely via
skip_serializing_if = "Option::is_none", so the v4 SDK degrades
cleanly to None on a v3 backend — no false positive.

Source-side wiring:

1. NULLRUN_PROTOCOL_VERSION = 4 (src/nullrun/transport.py). The
   X-NULLRUN-PROTOCOL header now serialises the bumped value via
   the single source of truth; tests pin to str(NULLRUN_PROTOCOL_VERSION)
   so a future bump doesn't sweep this file again.
   NullRunProtocolError.user_action and docs/errors/NR-P001.md
   updated to point operators at X-NULLRUN-PROTOCOL: 4.

2. Two new contextvars in src/nullrun/context.py —
   _last_gate_action_digest_var, _last_gate_policy_hash_var — with
   public accessors get_last_gate_action_digest() /
   get_last_gate_policy_hash() and setters set_last_gate_action_digest() /
   set_last_gate_policy_hash(). clear_server_minted_execution_id
   also drops the v4 slots so a /check in one block never leaks
   a stale echo into a /track in a sibling block.

3. runtime._capture_wire_evidence (extracted from
   _capture_server_minted_execution_id so the two captures share
   a call site but have distinct log lines). Reads action_digest
   + policy_hash off the response, defensively validates type
   (drops non-str at WARNING), and stores into the contextvars.
   Called from _capture_server_minted_execution_id on the same
   /check lifetime so execution_id and action_digest always refer
   to the same gate decision.

4. ServerCapabilities.wire_evidence_echo (src/nullrun/capabilities.py) —
   informational flag surfaced by /api/v1/capabilities. NOT
   included in is_v3_ready() (informational, not a hard gate).
   Defaults to False on pre-v4 backends.

5. tests/test_slice_b_wire_evidence.py (10 new tests). Pins the
   SDK-side of the v3→4 additive bump: protocol-constant value,
   header serialisation, capture from /gate response (happy path,
   policy_hash-when-present, both-set), tolerance of pre-v4
   backends, tolerance of malformed wire values, tolerance of
   non-dict response, clear semantics, source-of-truth wiring.

6. tests/test_capabilities.py — 2 new assertions:
   test_parse_capabilities_wire_evidence_echo_v4_backend and
   test_parse_capabilities_v4_protocol_range.

7. tests/contract/test_audit_wire.py + tests/test_v3_wire_contract.py
   — header assertions now source str(NULLRUN_PROTOCOL_VERSION)
   instead of the literal "3" so a future bump doesn't require
   sweeping either file. Class names kept for git-blame continuity.

8. README.md — alpha-status line + roadmap table updated:
   v0.15 → v0.15.x, v0.16 → v0.16.x with the new highlights,
   v0.17 for OpenTelemetry exporter / Redis-backed offline queue /
   hardened init contract that previously sat under v0.16.

* docs: NR-P001 user_action bumps to protocol 4 reference

NullRunProtocolError.user_action and docs/errors/NR-P001.md
updated to point operators at X-NULLRUN-PROTOCOL: 4 (was 3).
The v3→v4 bump is additive: min_protocol_version stays at 2,
so v3 SDKs continue to work against a v4 backend. The new
user_action tells operators running a pre-v4 SDK against a
v4+ backend to upgrade.

* chore(release): 0.16.4 — version bump + CHANGELOG

- pyproject.toml + src/nullrun/__version__.py: 0.16.3 → 0.16.4.
- CHANGELOG.md: insert [0.16.4] - 2026-08-31 at the top with full
  descriptions of the Slice B wire-evidence echo (capture helper,
  contextvars, capabilities flag, test pins), the protocol version
  bump, the wire-format additive nature, and the 1613/4 pytest
  + ruff-clean + mypy-clean verification status.

Verified: pytest 1613 passed / 4 skipped (12 more than 0.16.3,
accounting for the 10 new Slice B pins + 2 new capabilities
assertions); ruff check src tests all checks pass; mypy src/nullrun
no issues reported in 37 source files. No new wire fields beyond
the two echoed slots — both fields echo already-computed values,
no new hashing/computation introduced on either side.

Refs: ADR-037 Slice B (2026-08-31); backend commit 8dbeaf4d
(parity CI test analogue for SDK side); docs/errors/NR-P001.md.
@maltsev-dev
maltsev-dev merged commit 8670d53 into master Sep 1, 2026
4 checks passed
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant